#pragma
#include "log.hpp"
#include <iostream>
#include <cstdio>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <pthread.h>


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <wait.h>

#define SIZE 1024
#define BACKLOG 5


//介绍：多线程版本

//多线程是公用同一个文件描述符表的，可以直接去访问文件描述符。当accpet获取到一个连接之后，其他的线程都是公用该连接的，可以去服务该连接。





class Param//
{
public:
    Param(int sock,std::string ip,int port)
    :_server_sock(sock)
    ,_ip(ip)
    ,_port(port)
    {
        ;
    }
    ~Param()
    {
        ;
    }
public:
    int _server_sock;
    std::string _ip;
    int _port;
};





class TcpServer
{
public:
    TcpServer(uint16_t port, std::string ip = "")
        : _port(port),
          _ip(ip),
          _listen_sock(-1)
    {
        ;
    }

    void initServer()
    {
        // 1.创建套接字
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0); // 协议家族，服务类型(tcp是字节流，udp是数据报头)，协议类别(指明是tcp or udp，指明会自己默认自动识别)
        if (_listen_sock < 0)
        {
            logMessage(FATAL, "%d,%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "socket success ... sockfd:%d ,strerror:%s", _listen_sock, strerror(errno));

        struct sockaddr_in local;     // 写入服务端信息
        bzero(&local, sizeof(local)); // 将local全部字节清0

        ////struct sockaddr_in  本质
        //{
        // unsigned short int sin_family;
        // unsigned short int sin_port;
        // unsigned int sin_ip;
        //}
        ////

        local.sin_family = AF_INET;
        // host to net by short.由 主机序列(unsigned short int) 转为 网络字节序 (unsigned short int)
        local.sin_port = htons(_port);
        //_ip若为空，则指定从任意ip上接收数据。若_ip不为空，则将ip由 主机序列(点分十进制的字符串(c语言风格))类型 转为 网络序列(unsigned int)
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());

        // 2.绑定
        if (bind(_listen_sock, (struct sockaddr *)&local, sizeof local) < 0)
        {
            logMessage(FATAL, " %d %s ", errno, strerror(errno)); // 表示绑定失败，说明指定的ip和port 与 指定的套接字 绑定失败了
            exit(3);
        }
        logMessage(NORMAL, "bind success ... %s", strerror(errno));

        // 3.设置监听状态并设置等待队列长度，为accpet获取链接做准备
        if (listen(_listen_sock, BACKLOG) < 0)
        {
            logMessage(ERROR, "listen error ... %s", strerror(errno));
            exit(4);
        }
        logMessage(NORMAL, "listen set success ... %s", strerror(errno));

        logMessage(NORMAL, "init tcp server done ... %s", strerror(errno));
        // done
        return;
    }



    


    //由于线程例程函数是静态成员函数，没办法调用非静态成员函数，所以我们需要将Service函数改为static静态成员函数，并且正好Service函数执行的内容是和类
    //无关的，所以不需要this指针。此时是静态成员函数的例程函数就可以直接调用Service函数了。
    static void Service(int sock, std::string client_ip, int client_port)
    {
        char buffer[1024];
        while (true)
        {
            ssize_t size = read(sock, buffer, sizeof(buffer) - 1);
            if (size > 0)
            {
                // 读取成功
                buffer[size] = '\0';
                std::cout << "get a new link-> " << sock << " [" << client_ip << "] " << client_port <<"# "<<buffer<<std::endl;

                write(sock, buffer, size);
            }
            else if (size == 0)
            {
                // 对端连接关闭
                std::cout << client_ip << ":" << client_ip << " close!" << std::endl;
                break;
            }
            else
            {
                // 读取失败
                std::cerr << sock << " read error!" << std::endl;
                break;
            }
        }
        close(sock); // 归还文件描述符
        std::cout << client_ip << ":" << client_port << " server done!" << std::endl;
    }

    static void* HandlerRequest(void* arg)
    {
        pthread_detach(pthread_self());//pthread_self()获取当前线程的tid，然后交给pthread_detach进行线程分离，之后主线程就不用回收该新线程了，新线程会交给内核回收
        Param* p=(Param*)arg;

        Service(p->_server_sock,p->_ip,p->_port);
    
        delete p;
        return nullptr;
    }

    void Start()
    {
        char buffer[SIZE]; // 用于接收客户端发来的数据
        while (true)       // 永远不会退出，除非服务端挂了
        {
            // 4.accpet获取连接

            struct sockaddr_in peer; // 用于存放获取到的连接中的对端的网络相关的属性信息
            bzero(&peer, sizeof(peer));
            socklen_t len = sizeof(peer);

            int _server_sock = accept(_listen_sock, (struct sockaddr *)&peer, &len); // 输入型参数-监听套接字，输出型参数-对端网络信息，输入输出型参数-peer的大小

            if (_server_sock < 0)
            {
                logMessage(ERROR,"accpet error,continue next");
                continue;
            }

            std::string client_ip = inet_ntoa(peer.sin_addr); // 结构体封装的unsigned int 转 字符串char*。（字符串char*会隐式类型转化为string)
            uint16_t client_port = ntohs(peer.sin_port);      // unsigned short int 转 unsigned short int
            std::cout << "get a new link-> " << _server_sock << " [" << client_ip << "] " << client_port << std::endl;
 
            Param* p=new Param(_server_sock,client_ip,client_port);//线程的函数的参数
            pthread_t tid;
            pthread_create(&tid,nullptr,HandlerRequest,p);
        }
    }
    ~TcpServer()
    {
        if (_listen_sock >= 0)
            close(_listen_sock);
    }

private:
    uint16_t _port;   // 端口号 unsigned short int
    std::string _ip;  // ip地址
    int _listen_sock; // 套接字
};
